//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package roadmap_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas-org/api-server/dao/db_ns"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/roadmap_content_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
)

type NodeDataTextDbItem struct {
	FontSize   uint32 `bson:"fontSize"`
	FontWeight uint32 `bson:"fontWeight"`
	FontColor  string `bson:"fontColor"`
	Content    string `bson:"content"`
	BgColor    string `bson:"bgColor"`
}

type NodeDataRichTextDbItem struct {
	Content string `bson:"content"`
	BgColor string `bson:"bgColor"`
}

type ExtraLinkItemDbItem struct {
	LinkId    string `bson:"linkId"`
	LinkType  uint32 `bson:"linkType"`
	LinkTitle string `bson:"linkTitle"`
	LinkUrl   string `bson:"linkUrl"`
}

type NodeDataTopicDbItem struct {
	FontSize    uint32                `bson:"fontSize"`
	FontWeight  uint32                `bson:"fontWeight"`
	FontColor   string                `bson:"fontColor"`
	Title       string                `bson:"title"`
	BgColor     string                `bson:"bgColor"`
	Content     string                `bson:"content"`
	LearnPolicy uint32                `bson:"learnPolicy"`
	LinkList    []ExtraLinkItemDbItem `bson:"linkList"`
}

type NodeTodoItemDbItem struct {
	TodoId string `bson:"todoId"`
	Title  string `bson:"title"`
}

type NodeDataTodoListDbItem struct {
	ItemList []NodeTodoItemDbItem `bson:"itemList"`
	BgColor  string               `bson:"bgColor"`
}

type NodeDataGroupDbItem struct {
	BgColor      string `bson:"bgColor"`
	BorderColor  string `bson:"borderColor"`
	BorderRadius uint32 `bson:"borderRadius"`
}

type NodeDataRoadmapRefDbItem struct {
	TargetTitle string `bson:"targetTitle"`
	TargetId    string `bson:"targetId"`
}

type NodeDataWebLinkDbItem struct {
	LinkName string `bson:"linkName"`
	LinkUrl  string `bson:"linkUrl"`
}

type NodeDataCodeDbItem struct {
	Lang string `bson:"lang"`
	Code string `bson:"code"`
}

type BasicNodeInfoDbItem struct {
	NodeType uint32  `bson:"nodeType"`
	W        float32 `bson:"w"`
	H        float32 `bson:"h"`
	X        float32 `bson:"x"`
	Y        float32 `bson:"y"`

	TextData       *NodeDataTextDbItem       `bson:"textData"`
	RichTextData   *NodeDataRichTextDbItem   `bson:"richTextData"`
	TopicData      *NodeDataTopicDbItem      `bson:"topicData"`
	TodoListData   *NodeDataTodoListDbItem   `bson:"todoListData"`
	GroupData      *NodeDataGroupDbItem      `bson:"groupData"`
	RoadmapRefData *NodeDataRoadmapRefDbItem `bson:"roadmapRefData"`
	WebLinkData    *NodeDataWebLinkDbItem    `bson:"webLinkData"`
	CodeData       *NodeDataCodeDbItem       `bson:"codeData"`
}

type NodeInfoDbItem struct {
	ItemId    string              `bson:"_id"` //自动生成
	NodeId    string              `bson:"nodeId"`
	BasicInfo BasicNodeInfoDbItem `bson:"basicInfo"`
	RoadmapId string              `bson:"roadmapId"`
}

func (item *NodeInfoDbItem) ToNodeInfo() *roadmap_content_api.NodeInfo {
	retItem := &roadmap_content_api.NodeInfo{
		NodeId: item.NodeId,
		BasicInfo: &roadmap_content_api.BasicNodeInfo{
			NodeType: roadmap_content_api.NODE_TYPE(item.BasicInfo.NodeType),
			NodeSize: &roadmap_content_api.NodeSize{
				W: item.BasicInfo.W,
				H: item.BasicInfo.H,
			},
			NodePosition: &roadmap_content_api.NodePosition{
				X: item.BasicInfo.X,
				Y: item.BasicInfo.Y,
			},
		},
		RoadmapId: item.RoadmapId,
	}
	if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_LABEL) {
		if item.BasicInfo.TextData == nil {
			item.BasicInfo.TextData = &NodeDataTextDbItem{
				FontSize:   14,
				FontWeight: 500,
				FontColor:  "black",
				Content:    "",
				BgColor:    "white",
			}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_TextData{
			TextData: &roadmap_content_api.NodeDataText{
				FontSize:   item.BasicInfo.TextData.FontSize,
				FontWeight: item.BasicInfo.TextData.FontWeight,
				FontColor:  item.BasicInfo.TextData.FontColor,
				Content:    item.BasicInfo.TextData.Content,
				BgColor:    item.BasicInfo.TextData.BgColor,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_RICH_TEXT) {
		if item.BasicInfo.RichTextData == nil {
			item.BasicInfo.RichTextData = &NodeDataRichTextDbItem{
				Content: "",
				BgColor: "white",
			}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_RichTextData{
			RichTextData: &roadmap_content_api.NodeDataRichText{
				Content: item.BasicInfo.RichTextData.Content,
				BgColor: item.BasicInfo.RichTextData.BgColor,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_TOPIC) || item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_SUB_TOPIC) {
		if item.BasicInfo.TopicData == nil {
			item.BasicInfo.TopicData = &NodeDataTopicDbItem{
				FontSize:    14,
				FontWeight:  500,
				FontColor:   "black",
				Title:       "",
				BgColor:     "white",
				Content:     "",
				LearnPolicy: uint32(roadmap_content_api.LEARN_POLICY_TYPE_LEARN_POLICY_NONE),
				LinkList:    []ExtraLinkItemDbItem{},
			}
		}
		linkList := []*roadmap_content_api.ExtraLinkItem{}
		if item.BasicInfo.TopicData.LinkList != nil {
			for _, linkItem := range item.BasicInfo.TopicData.LinkList {
				linkList = append(linkList, &roadmap_content_api.ExtraLinkItem{
					LinkId:    linkItem.LinkId,
					LinkType:  roadmap_content_api.EXTRA_LINK_TYPE(linkItem.LinkType),
					LinkTitle: linkItem.LinkTitle,
					LinkUrl:   linkItem.LinkUrl,
				})
			}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_TopicData{
			TopicData: &roadmap_content_api.NodeDataTopic{
				FontSize:    item.BasicInfo.TopicData.FontSize,
				FontWeight:  item.BasicInfo.TopicData.FontWeight,
				FontColor:   item.BasicInfo.TopicData.FontColor,
				Title:       item.BasicInfo.TopicData.Title,
				BgColor:     item.BasicInfo.TopicData.BgColor,
				Content:     item.BasicInfo.TopicData.Content,
				LearnPolicy: roadmap_content_api.LEARN_POLICY_TYPE(item.BasicInfo.TopicData.LearnPolicy),
				LinkList:    linkList,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_TODO_LIST) {
		if item.BasicInfo.TodoListData == nil {
			item.BasicInfo.TodoListData = &NodeDataTodoListDbItem{
				ItemList: []NodeTodoItemDbItem{},
				BgColor:  "white",
			}
		}
		todoList := []*roadmap_content_api.NodeTodoItem{}
		for _, todoItem := range item.BasicInfo.TodoListData.ItemList {
			todoList = append(todoList, &roadmap_content_api.NodeTodoItem{
				TodoId: todoItem.TodoId,
				Title:  todoItem.Title,
			})
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_TodoListData{
			TodoListData: &roadmap_content_api.NodeDataTodoList{
				ItemList: todoList,
				BgColor:  item.BasicInfo.TodoListData.BgColor,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_GROUP) {
		if item.BasicInfo.GroupData == nil {
			item.BasicInfo.GroupData = &NodeDataGroupDbItem{
				BgColor:      "white",
				BorderColor:  "black",
				BorderRadius: 0,
			}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_GroupData{
			GroupData: &roadmap_content_api.NodeDataGroup{
				BgColor:      item.BasicInfo.GroupData.BgColor,
				BorderColor:  item.BasicInfo.GroupData.BorderColor,
				BorderRadius: item.BasicInfo.GroupData.BorderRadius,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_ROADMAP_REF) {
		if item.BasicInfo.RoadmapRefData == nil {
			item.BasicInfo.RoadmapRefData = &NodeDataRoadmapRefDbItem{}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_RoadmapRefData{
			RoadmapRefData: &roadmap_content_api.NodeDataRoadmapRef{
				TargetTitle: item.BasicInfo.RoadmapRefData.TargetTitle,
				TargetId:    item.BasicInfo.RoadmapRefData.TargetId,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_WEB_LINK) {
		if item.BasicInfo.WebLinkData == nil {
			item.BasicInfo.WebLinkData = &NodeDataWebLinkDbItem{}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_WebLinkData{
			WebLinkData: &roadmap_content_api.NodeDataWebLink{
				LinkName: item.BasicInfo.WebLinkData.LinkName,
				LinkUrl:  item.BasicInfo.WebLinkData.LinkUrl,
			},
		}
	} else if item.BasicInfo.NodeType == uint32(roadmap_content_api.NODE_TYPE_NODE_CODE) {
		if item.BasicInfo.CodeData == nil {
			item.BasicInfo.CodeData = &NodeDataCodeDbItem{}
		}
		retItem.BasicInfo.NodeData = &roadmap_content_api.BasicNodeInfo_CodeData{
			CodeData: &roadmap_content_api.NodeDataCode{
				Lang: item.BasicInfo.CodeData.Lang,
				Code: item.BasicInfo.CodeData.Code,
			},
		}
	}
	return retItem
}

func GenNodeItemId(roadmapId, nodeId string) string {
	return fmt.Sprintf("%s:%s", roadmapId, nodeId)
}

type _NodeDao struct {
	mc *mongo.Collection
}

func newNodeDao(db *mongo.Database) (*_NodeDao, error) {
	mc := db.Collection(db_ns.DB_NS_ROADMAP + "node")
	_, err := mc.Indexes().CreateOne(context.Background(), mongo.IndexModel{
		Keys: bson.D{{"roadmapId", 1}},
	})
	if err != nil {
		return nil, err
	}
	return &_NodeDao{mc: mc}, nil
}

func (db *_NodeDao) Insert(ctx context.Context, dbItem *NodeInfoDbItem) error {
	dbItem.ItemId = GenNodeItemId(dbItem.RoadmapId, dbItem.NodeId)
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_NodeDao) Exist(ctx context.Context, roadmapId, nodeId string) (bool, error) {
	itemId := GenNodeItemId(roadmapId, nodeId)
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId})
	return count > 0, err
}

func (db *_NodeDao) Get(ctx context.Context, roadmapId, nodeId string) (*NodeInfoDbItem, error) {
	itemId := GenNodeItemId(roadmapId, nodeId)
	retItem := &NodeInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId}).Decode(retItem)
	return retItem, err
}

func (db *_NodeDao) Remove(ctx context.Context, roadmapId, nodeId string) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.DeleteOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId})
	return err
}

func (db *_NodeDao) RemoveByRoadmap(ctx context.Context, roadmapId string) error {
	_, err := db.mc.DeleteMany(ctx, bson.M{"roadmapId": roadmapId})
	return err
}

func (db *_NodeDao) List(ctx context.Context, roadmapId string) ([]*NodeInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"roadmapId": roadmapId})
	if err != nil {
		return nil, err
	}
	retList := []*NodeInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_NodeDao) ListById(ctx context.Context, itemIdList []string) ([]*NodeInfoDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": itemIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*NodeInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_NodeDao) UpdateSize(ctx context.Context, roadmapId, nodeId string, w, h float32) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.w": w, "basicInfo.h": h}})
	return err
}

func (db *_NodeDao) UpdatePosition(ctx context.Context, roadmapId, nodeId string, x, y float32) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.x": x, "basicInfo.y": y}})
	return err
}

func (db *_NodeDao) UpdateTextData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataTextDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.textData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateRichTextData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataRichTextDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.richTextData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateTopicData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataTopicDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.topicData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateTodoListData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataTodoListDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.todoListData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateGroupData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataGroupDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.groupData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateRoadmapRefData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataRoadmapRefDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.roadmapRefData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateWebLinkData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataWebLinkDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.webLinkData": dataDbItem}})
	return err
}

func (db *_NodeDao) UpdateCodeData(ctx context.Context, roadmapId, nodeId string, dataDbItem *NodeDataCodeDbItem) error {
	itemId := GenNodeItemId(roadmapId, nodeId)
	_, err := db.mc.UpdateOne(ctx, bson.M{"_id": itemId, "roadmapId": roadmapId},
		bson.M{"$set": bson.M{"basicInfo.codeData": dataDbItem}})
	return err
}

func (db *_NodeDao) DistinctItemId(ctx context.Context, itemIdList []string) ([]string, error) {
	tmpItemList, err := db.mc.Distinct(ctx, "_id", bson.M{"_id": bson.M{"$in": itemIdList}})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpItem := range tmpItemList {
		retList = append(retList, tmpItem.(string))
	}
	return retList, nil
}
